import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import json
import os
import time
from datetime import datetime
from pathlib import Path
import threading
import queue
import asyncio
import pickle

from bluetooth_listener import start_bluetooth_listener
from face_recognizer import FaceRecognizer

# 设置页面配置
st.set_page_config(
    page_title="基于ESP32-S3的人脸识别与体感动作交互的智能课堂互动系统",
    page_icon="📊",
    layout="wide",
    initial_sidebar_state="expanded"
)

# 样式设置
st.markdown("""
<style>
    /* 完全重新设计标题样式 */
    .main-header {
        font-size: 2.5rem;
        color: #0D47A1;
        text-align: center;
        margin-bottom: 1.5rem;
        font-weight: 700;
        padding: 1rem 0;
        line-height: 1.4;
        letter-spacing: 0.5px;
    }
    
    .title-highlight {
        background-color: #E3F2FD;
        padding: 5px 10px;
        border-radius: 4px;
        color: #1565C0;
        white-space: nowrap;
    }
    
    .title-container {
        margin-bottom: 1rem;
        border: 2px solid #1E88E5;
        border-radius: 12px;
        padding: 1.5rem;
        background-color: white;
        box-shadow: 0 4px 12px rgba(0,0,0,0.1);
    }
    
    .subtitle {
        font-size: 1.2rem;
        color: #424242;
        font-weight: 600;
        text-align: center;
        margin-bottom: 1rem;
    }
    .sub-header {
        font-size: 1.8rem;
        color: #0D47A1;
        margin-top: 1.5rem;
        margin-bottom: 1rem;
        border-bottom: 2px solid #E3F2FD;
        padding-bottom: 0.5rem;
        font-weight: 600;
    }
    .info-text {
        font-size: 1.2rem;
        line-height: 1.6;
    }
    .highlight {
        background-color: #E3F2FD;
        padding: 1.2rem;
        border-radius: 0.7rem;
        margin: 1.2rem 0;
        box-shadow: 0 2px 5px rgba(0,0,0,0.05);
        border-left: 4px solid #1E88E5;
        position: relative;
        overflow: hidden;
    }
    .highlight::before {
        content: "";
        position: absolute;
        top: 0;
        right: 0;
        width: 25%;
        height: 100%;
        background: linear-gradient(90deg, transparent, rgba(66, 165, 245, 0.05));
    }
    .student-card {
        padding: 1rem;
        border-radius: 0.7rem;
        margin: 0.7rem 0;
        background-color: #F5F5F5;
        transition: all 0.3s;
        border: 1px solid #E0E0E0;
        position: relative;
        overflow: hidden;
    }
    .student-card:hover {
        box-shadow: 0 4px 10px rgba(0,0,0,0.1);
        transform: translateY(-2px);
        background-color: #FAFAFA;
    }
    .active-student {
        background-color: #E3F2FD;
        border-left: 5px solid #1E88E5;
        font-weight: 500;
        box-shadow: 0 3px 8px rgba(0,0,0,0.08);
        animation: pulse 2s infinite;
    }
    @keyframes pulse {
        0% { box-shadow: 0 3px 8px rgba(0,0,0,0.08); }
        50% { box-shadow: 0 3px 15px rgba(30, 136, 229, 0.3); }
        100% { box-shadow: 0 3px 8px rgba(0,0,0,0.08); }
    }
    .metric-card {
        background-color: #F5F5F5;
        padding: 1.5rem;
        border-radius: 1rem;
        text-align: center;
        box-shadow: 0 3px 6px rgba(0,0,0,0.08);
        transition: all 0.3s;
        border: 1px solid #EEEEEE;
        position: relative;
        overflow: hidden;
    }
    .metric-card::after {
        content: "";
        position: absolute;
        bottom: 0;
        left: 0;
        width: 100%;
        height: 3px;
        background: linear-gradient(90deg, #1E88E5, #42A5F5);
        transform: scaleX(0);
        transform-origin: left;
        transition: transform 0.3s ease-in-out;
    }
    .metric-card:hover {
        transform: translateY(-3px);
        box-shadow: 0 5px 15px rgba(0,0,0,0.1);
    }
    .metric-card:hover::after {
        transform: scaleX(1);
    }
    .metric-value {
        font-size: 2.5rem;
        font-weight: bold;
        color: #1E88E5;
        margin-bottom: 0.5rem;
    }
    .metric-label {
        font-size: 1.1rem;
        color: #424242;
        font-weight: 500;
    }
    /* 按钮样式优化 */
    .stButton>button {
        border-radius: 0.5rem;
        font-weight: 500;
        padding: 0.5rem 1rem;
        transition: all 0.3s;
        box-shadow: 0 2px 5px rgba(0,0,0,0.05);
        position: relative;
        overflow: hidden;
    }
    .stButton>button::before {
        content: "";
        position: absolute;
        top: 0;
        left: -100%;
        width: 100%;
        height: 100%;
        background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
        transition: 0.5s;
    }
    .stButton>button:hover {
        transform: translateY(-2px);
        box-shadow: 0 4px 8px rgba(0,0,0,0.1);
    }
    .stButton>button:hover::before {
        left: 100%;
    }
    /* 表格样式优化 */
    .stDataFrame {
        border-radius: 0.7rem;
        overflow: hidden;
        border: 1px solid #E0E0E0;
    }
    /* 信息提示样式 */
    .stInfo {
        border-radius: 0.7rem;
        padding: 1rem;
    }
    /* 图表区域样式 */
    .chart-container {
        background-color: white;
        padding: 1.5rem;
        border-radius: 0.7rem;
        box-shadow: 0 2px 8px rgba(0,0,0,0.05);
        margin: 1rem 0;
        border: 1px solid #F0F0F0;
        transition: all 0.3s;
    }
    .chart-container:hover {
        box-shadow: 0 4px 12px rgba(0,0,0,0.1);
    }
    /* 自定义消息框样式 */
    .custom-message {
        padding: 1rem;
        border-radius: 0.7rem;
        margin: 1rem 0;
        position: relative;
        padding-left: 3rem;
        animation: slideIn 0.3s ease-out;
    }
    .custom-message.success {
        background-color: #E8F5E9;
        border-left: 4px solid #4CAF50;
        color: #2E7D32;
    }
    .custom-message.info {
        background-color: #E3F2FD;
        border-left: 4px solid #2196F3;
        color: #0D47A1;
    }
    .custom-message.warning {
        background-color: #FFF8E1;
        border-left: 4px solid #FFC107;
        color: #FF8F00;
    }
    .custom-message.error {
        background-color: #FFEBEE;
        border-left: 4px solid #F44336;
        color: #C62828;
    }
    .custom-message::before {
        font-family: sans-serif;
        position: absolute;
        left: 1rem;
        font-size: 1.2rem;
    }
    .custom-message.success::before {
        content: "✓";
        color: #4CAF50;
    }
    .custom-message.info::before {
        content: "ℹ";
        color: #2196F3;
    }
    .custom-message.warning::before {
        content: "⚠";
        color: #FFC107;
    }
    .custom-message.error::before {
        content: "✕";
        color: #F44336;
    }
    @keyframes slideIn {
        from { opacity: 0; transform: translateY(-10px); }
        to { opacity: 1; transform: translateY(0); }
    }
    /* 页面背景和容器样式 */
    .main {
        background-color: #F9FAFC;
    }
    .block-container {
        padding: 2rem 3rem;
    }
    /* 选项卡样式 */
    .stTabs [data-baseweb="tab-list"] {
        gap: 1rem;
    }
    .stTabs [data-baseweb="tab"] {
        border-radius: 4px 4px 0 0;
        padding: 0.5rem 1rem;
        border: none !important;
    }
    .stTabs [aria-selected="true"] {
        background-color: #E3F2FD !important;
        color: #1E88E5 !important;
        font-weight: 500;
    }
</style>
""", unsafe_allow_html=True)

# 数据路径设置
DATA_DIR = Path("data")
FACE_DATA_FILE = DATA_DIR / "face_data.json"
INTERACTION_DATA_FILE = DATA_DIR / "interactions.csv"
SESSION_STATE_FILE = DATA_DIR / "session_state.pkl"

# 确保数据目录存在
DATA_DIR.mkdir(exist_ok=True)

# 初始化必要的对象
if "recognizer" not in st.session_state:
    st.session_state.recognizer = FaceRecognizer()

if "message_queue" not in st.session_state:
    st.session_state.message_queue = queue.Queue()

# 初始化会话状态标志
if "initialized" not in st.session_state:
    # 尝试加载之前的会话状态
    if os.path.exists(SESSION_STATE_FILE):
        try:
            with open(SESSION_STATE_FILE, 'rb') as f:
                loaded_state = pickle.load(f)
                for key, value in loaded_state.items():
                    st.session_state[key] = value
            st.session_state.initialized = True
        except Exception as e:
            st.error(f"加载会话状态失败: {e}")
            st.session_state.initialized = False
    else:
        st.session_state.initialized = False

# 如果没有初始化，设置默认状态
if not st.session_state.initialized:
    st.session_state.student_data = {}
    st.session_state.interaction_records = []
    st.session_state.bluetooth_running = False
    st.session_state.total_interactions = 0
    st.session_state.recognized_students = set()
    st.session_state.last_update = datetime.now()
    
    # 尝试加载已有的学生数据
    face_data = load_face_data()
    if face_data:
        st.session_state.student_data = face_data
        st.session_state.recognizer.load_face_data(face_data)
        
    # 尝试加载已有的互动记录
    try:
        interaction_df = load_interaction_records()
        if not interaction_df.empty:
            st.session_state.interaction_records = interaction_df.to_dict('records')
            st.session_state.total_interactions = len(st.session_state.interaction_records)
            st.session_state.recognized_students = set(interaction_df['学号'].unique())
    except Exception as e:
        print(f"加载互动记录失败: {e}")
    
    st.session_state.initialized = True

# 保存会话状态
def save_session_state():
    save_data = {
        "student_data": st.session_state.student_data,
        "interaction_records": st.session_state.interaction_records,
        "bluetooth_running": st.session_state.bluetooth_running,
        "total_interactions": st.session_state.total_interactions,
        "recognized_students": st.session_state.recognized_students,
        "last_update": st.session_state.last_update
    }
    with open(SESSION_STATE_FILE, 'wb') as f:
        pickle.dump(save_data, f)

# 加载学生面部数据
def load_face_data():
    if FACE_DATA_FILE.exists():
        with open(FACE_DATA_FILE, 'r', encoding='utf-8') as f:
            return json.load(f)
    return {}

# 保存学生面部数据
def save_face_data(data):
    with open(FACE_DATA_FILE, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

# 加载互动记录
def load_interaction_records():
    if INTERACTION_DATA_FILE.exists():
        return pd.read_csv(INTERACTION_DATA_FILE)
    return pd.DataFrame(columns=["学号", "姓名", "互动时间"])

# 保存互动记录
def save_interaction_records(records):
    df = pd.DataFrame(records, columns=["学号", "姓名", "互动时间"])
    df.to_csv(INTERACTION_DATA_FILE, index=False)

# 更新学生互动记录
def update_interaction(student_id, timestamp=None):
    if timestamp is None:
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    student_name = "未知"
    if student_id in st.session_state.student_data:
        student_name = st.session_state.student_data[student_id].get("name", "未知")
    
    new_record = {
        "学号": student_id,
        "姓名": student_name,
        "互动时间": timestamp
    }
    
    print(f"添加互动记录: {new_record}")
    
    # 添加记录到会话状态
    st.session_state.interaction_records.append(new_record)
    st.session_state.total_interactions += 1
    st.session_state.recognized_students.add(student_id)
    st.session_state.last_update = datetime.now()
    
    # 保存记录到文件
    try:
        save_interaction_records(st.session_state.interaction_records)
        print(f"已保存互动记录，当前总记录数: {len(st.session_state.interaction_records)}")
    except Exception as e:
        print(f"保存互动记录失败: {e}")
    
    # 保存会话状态
    try:
        save_session_state()
        print("已保存会话状态")
    except Exception as e:
        print(f"保存会话状态失败: {e}")
    
    # 强制页面刷新（仅在需要时添加）
    # st.experimental_rerun()
    
    return new_record

# 处理蓝牙消息的线程函数
def process_bluetooth_messages():
    while st.session_state.bluetooth_running:
        try:
            if not st.session_state.message_queue.empty():
                message = st.session_state.message_queue.get(block=False)
                
                # 假设消息是一个JSON字符串，包含人脸特征数据
                try:
                    data = json.loads(message)
                    # 调用人脸识别器进行比对
                    student_id = st.session_state.recognizer.recognize_face(data)
                    # 更新互动记录
                    update_interaction(student_id)
                except json.JSONDecodeError:
                    print(f"无效的JSON数据: {message}")
                except Exception as e:
                    print(f"处理消息时出错: {e}")
            else:
                time.sleep(0.1)  # 避免CPU空转
        except Exception as e:
            print(f"消息处理线程错误: {e}")
            time.sleep(1)  # 发生错误时暂停一下

# 启动蓝牙监听
def start_bluetooth():
    if not st.session_state.bluetooth_running:
        # 加载人脸数据到识别器
        face_data = load_face_data()
        st.session_state.recognizer.load_face_data(face_data)
        
        # 启动蓝牙监听线程
        st.session_state.bluetooth_running = True
        threading.Thread(target=process_bluetooth_messages, daemon=True).start()
        
        # 启动异步蓝牙监听
        loop = asyncio.new_event_loop()
        threading.Thread(
            target=start_bluetooth_listener,
            args=(loop, st.session_state.message_queue),
            daemon=True
        ).start()
        
        save_session_state()
        return "蓝牙监听已启动"
    return "蓝牙监听已经在运行中"

# 停止蓝牙监听
def stop_bluetooth():
    if st.session_state.bluetooth_running:
        st.session_state.bluetooth_running = False
        save_session_state()
        return "蓝牙监听已停止"
    return "蓝牙监听未运行"

# 导出数据
def export_data():
    if st.session_state.interaction_records:
        df = pd.DataFrame(st.session_state.interaction_records)
        return df.to_csv(index=False).encode('utf-8')
    return None

# 自定义消息提示函数
def custom_message(message, type="info"):
    """显示自定义美观的消息提示
    
    参数:
        message (str): 消息内容
        type (str): 消息类型，可选值: "success", "info", "warning", "error"
    """
    st.markdown(f'<div class="custom-message {type}">{message}</div>', unsafe_allow_html=True)

# 添加示例学生数据（仅用于演示）
def add_sample_students():
    # 检查是否已经添加过学生数据
    if st.session_state.student_data:
        if set(st.session_state.student_data.keys()) >= {"20226701", "20226702", "20226703", "20226704", "20226705"}:
            return "示例学生数据已存在，无需重复添加"
    
    sample_data = {
        "20226701": {
            "name": "张三",
            "features": [0.12, -0.45, 0.78, 0.32, -0.65]  # 简化的特征向量
        },
        "20226702": {
            "name": "李四",
            "features": [0.23, -0.36, 0.65, 0.41, -0.52]
        },
        "20226703": {
            "name": "王五",
            "features": [0.34, -0.27, 0.59, 0.48, -0.39]
        },
        "20226704": {
            "name": "赵六",
            "features": [0.45, -0.18, 0.52, 0.56, -0.28]
        },
        "20226705": {
            "name": "孙七",
            "features": [0.56, -0.09, 0.43, 0.67, -0.17]
        }
    }
    st.session_state.student_data.update(sample_data)
    save_face_data(st.session_state.student_data)
    save_session_state()
    return "已成功添加5名示例学生数据"

# 模拟接收信号（仅用于演示）
def simulate_signal():
    if not st.session_state.student_data:
        return "请先添加学生数据"
    
    # 随机选择一个学生
    student_id = np.random.choice(list(st.session_state.student_data.keys()))
    student_data = st.session_state.student_data[student_id]
    
    # 创建稍微变化的特征向量（模拟接收到的数据）
    features = np.array(student_data["features"])
    noise = np.random.normal(0, 0.05, size=len(features))  # 小随机噪声
    received_features = features + noise
    
    # 创建模拟接收到的数据
    received_data = {
        "detection_result": [[62, 60, 202, 237]],
        "landmarks": {
            "left_eye": [107, 123],
            "right_eye": [170, 121],
            "nose": [144, 151],
            "mouth_left": [119, 197],
            "mouth_right": [167, 193]
        },
        "features": received_features.tolist()
    }
    
    try:
        # 确保人脸识别器已加载数据
        if not hasattr(st.session_state.recognizer, "face_data") or not st.session_state.recognizer.face_data:
            face_data = load_face_data()
            if not face_data:  # 如果还是没有数据，使用当前的学生数据
                face_data = st.session_state.student_data
            st.session_state.recognizer.load_face_data(face_data)
        
        # 如果蓝牙监听已启动，将数据放入队列由监听线程处理
        if st.session_state.bluetooth_running:
            st.session_state.message_queue.put(json.dumps(received_data))
            print("数据已放入队列")
        # 否则直接在当前线程中处理数据
        else:
            # 识别人脸并更新互动记录
            recognized_id = st.session_state.recognizer.recognize_face(received_data)
            update_interaction(recognized_id)
            print(f"直接识别结果: {recognized_id}")
        
        return f"已模拟接收到学生 {student_data['name']} 的信号"
    except Exception as e:
        error_msg = f"模拟信号处理出错: {str(e)}"
        print(error_msg)
        import traceback
        print(traceback.format_exc())
        return error_msg

# 界面构建
def main():
    # 标题 - 使用完全重新设计的清晰标题格式
    st.markdown("""
    <div class='title-container'>
        <h1 class='main-header'>
            <span class='title-highlight'>基于ESP32-S3的人脸识别与体感动作交互</span><br>
            智能课堂互动系统 - 信号接收端
        </h1>
        <p class='subtitle'>
            ESP32-S3 Face Recognition & Motion Interactive System for Smart Classroom
        </p>
    </div>
    """, unsafe_allow_html=True)
    
    # 分栏布局
    col1, col2 = st.columns([2, 3])
    
    with col1:
        st.markdown("<h2 class='sub-header'>系统控制</h2>", unsafe_allow_html=True)
        
        # 蓝牙监听控制
        bluetooth_status = "运行中" if st.session_state.bluetooth_running else "已停止"
        status_color = "#4CAF50" if st.session_state.bluetooth_running else "#F44336"
        status_icon = "✓" if st.session_state.bluetooth_running else "✕"
        st.markdown(f"<p class='info-text'>蓝牙监听状态: <strong style='color:{status_color}'>{status_icon} {bluetooth_status}</strong></p>", unsafe_allow_html=True)
        
        col1_1, col1_2 = st.columns(2)
        with col1_1:
            if st.button("启动监听", key="start_bt", use_container_width=True):
                message = start_bluetooth()
                custom_message(message, "success")
        with col1_2:
            if st.button("停止监听", key="stop_bt", use_container_width=True):
                message = stop_bluetooth()
                custom_message(message, "info")
        
        # 数据管理
        st.markdown("<h3 class='sub-header'>数据管理</h3>", unsafe_allow_html=True)
        
        col1_3, col1_4 = st.columns(2)
        with col1_3:
            if st.button("导出数据", key="export", use_container_width=True):
                csv_data = export_data()
                if csv_data:
                    current_time = datetime.now().strftime("%Y%m%d_%H%M%S")
                    filename = f"互动记录_{current_time}.csv"
                    st.download_button(
                        label="下载CSV",
                        data=csv_data,
                        file_name=filename,
                        mime="text/csv",
                    )
                    custom_message("数据已准备好，点击下载按钮保存文件", "success")
                else:
                    custom_message("没有可导出的数据", "warning")
        
        with col1_4:
            if st.button("清空记录", key="clear", use_container_width=True):
                if st.session_state.interaction_records:
                    st.session_state.interaction_records = []
                    st.session_state.total_interactions = 0
                    st.session_state.recognized_students = set()
                    save_interaction_records([])
                    save_session_state()
                    custom_message("已清空所有互动记录", "success")
                else:
                    custom_message("当前没有互动记录可清空", "info")
        
        # 仅用于演示的功能
        st.markdown("<div class='highlight'><h3 class='sub-header'>演示功能</h3>", unsafe_allow_html=True)
        
        col1_5, col1_6 = st.columns(2)
        with col1_5:
            if st.button("添加示例学生", key="add_sample", use_container_width=True):
                message = add_sample_students()
                if "已存在" in message:
                    custom_message(message, "info")
                else:
                    custom_message(message, "success")
        
        with col1_6:
            if st.button("模拟接收信号", key="simulate", use_container_width=True):
                if not st.session_state.student_data:
                    custom_message("请先添加示例学生数据", "warning")
                else:
                    message = simulate_signal()
                    if "出错" in message:
                        custom_message(message, "error")
                    else:
                        custom_message(message, "success")
                    # 使用新的rerun接口
                    st.rerun()
        
        st.markdown("</div>", unsafe_allow_html=True)
        
        # 显示学生列表
        st.markdown("<h3 class='sub-header'>学生列表</h3>", unsafe_allow_html=True)
        
        if st.session_state.student_data:
            for student_id, data in sorted(st.session_state.student_data.items()):
                active_class = "active-student" if student_id in st.session_state.recognized_students else "student-card"
                activity_status = "已参与" if student_id in st.session_state.recognized_students else "未参与"
                status_color = "#4CAF50" if student_id in st.session_state.recognized_students else "#9E9E9E"
                
                st.markdown(f"""
                <div class='{active_class}'>
                    <strong>{data.get('name', '未知')}</strong> ({student_id})
                    <span style="float:right; color:{status_color}; font-size:0.9rem;">{activity_status}</span>
                </div>
                """, unsafe_allow_html=True)
        else:
            custom_message("没有学生数据。请添加示例学生或导入学生数据。", "info")
    
    with col2:
        st.markdown("<h2 class='sub-header'>互动统计</h2>", unsafe_allow_html=True)
        
        # 显示关键指标
        col2_1, col2_2, col2_3 = st.columns(3)
        
        with col2_1:
            st.markdown(f"""
            <div class='metric-card'>
                <div class='metric-value'>{st.session_state.total_interactions}</div>
                <div class='metric-label'>总互动次数</div>
            </div>
            """, unsafe_allow_html=True)
        
        with col2_2:
            unique_students = len(st.session_state.recognized_students)
            st.markdown(f"""
            <div class='metric-card'>
                <div class='metric-value'>{unique_students}</div>
                <div class='metric-label'>参与学生数</div>
            </div>
            """, unsafe_allow_html=True)
        
        with col2_3:
            total_students = len(st.session_state.student_data) if st.session_state.student_data else 1
            participation_rate = round(unique_students / total_students * 100) if total_students > 0 else 0
            st.markdown(f"""
            <div class='metric-card'>
                <div class='metric-value'>{participation_rate}%</div>
                <div class='metric-label'>参与率</div>
            </div>
            """, unsafe_allow_html=True)
        
        # 创建互动统计图表
        st.markdown("<h3 class='sub-header'>互动时间线</h3>", unsafe_allow_html=True)
        
        if st.session_state.interaction_records:
            # 包装图表区域
            st.markdown("<div class='chart-container'>", unsafe_allow_html=True)
            
            # 获取互动数据
            df = pd.DataFrame(st.session_state.interaction_records)
            df['互动时间'] = pd.to_datetime(df['互动时间'])
            
            # 计算每分钟的互动次数
            df['分钟'] = df['互动时间'].dt.floor('min')
            interactions_by_minute = df.groupby('分钟').size().reset_index(name='互动次数')
            
            # 如果数据点太少，添加一些空白数据点以便更好地显示
            if len(interactions_by_minute) < 2:
                now = pd.Timestamp.now().floor('min')
                five_mins_ago = now - pd.Timedelta(minutes=5)
                new_index = pd.date_range(start=five_mins_ago, end=now, freq='min')
                interactions_by_minute = interactions_by_minute.set_index('分钟').reindex(new_index, fill_value=0).reset_index()
                interactions_by_minute.columns = ['分钟', '互动次数']
            
            # 绘制时间线图表
            fig, ax = plt.subplots(figsize=(10, 5))
            
            # 添加一个略微透明的区域图表增强视觉效果
            ax.fill_between(
                interactions_by_minute['分钟'],
                interactions_by_minute['互动次数'],
                alpha=0.3,
                color='#1E88E5'
            )
            
            # 绘制线图和点
            ax.plot(interactions_by_minute['分钟'], interactions_by_minute['互动次数'], 
                    marker='o', linestyle='-', color='#1E88E5', linewidth=2, markersize=8)
            
            # 美化图表样式
            ax.set_xlabel('时间', fontsize=12, fontweight='bold')
            ax.set_ylabel('互动次数', fontsize=12, fontweight='bold')
            ax.grid(True, linestyle='--', alpha=0.7)
            ax.set_title('实时课堂互动频率', fontsize=14, fontweight='bold', pad=15)
            ax.spines['top'].set_visible(False)
            ax.spines['right'].set_visible(False)
            
            # 自动调整日期标签
            fig.autofmt_xdate()
            
            # 背景颜色和样式
            ax.set_facecolor('#F8F9FA')
            fig.patch.set_facecolor('#FFFFFF')
            
            # 突出显示数据点
            for i, val in enumerate(interactions_by_minute['互动次数']):
                if val > 0:
                    ax.text(interactions_by_minute['分钟'].iloc[i], val + 0.1, str(val),
                            ha='center', va='bottom', fontweight='bold', color='#1E88E5')
            
            st.pyplot(fig)
            st.markdown("</div>", unsafe_allow_html=True)
            
            # 学生参与分布图
            st.markdown("<h3 class='sub-header'>学生参与分布</h3>", unsafe_allow_html=True)
            st.markdown("<div class='chart-container'>", unsafe_allow_html=True)
            
            student_counts = df.groupby(['学号', '姓名']).size().reset_index(name='互动次数')
            student_counts = student_counts.sort_values('互动次数', ascending=False)
            
            if not student_counts.empty:
                fig2, ax2 = plt.subplots(figsize=(10, 5))
                
                # 使用更漂亮的颜色
                colors = ['#1E88E5', '#42A5F5', '#64B5F6', '#90CAF9', '#BBDEFB']
                if len(student_counts) > len(colors):
                    colors = colors * (len(student_counts) // len(colors) + 1)
                
                bars = ax2.bar(
                    range(len(student_counts)), 
                    student_counts['互动次数'],
                    color=colors[:len(student_counts)],
                    width=0.6
                )
                
                # 添加标签
                ax2.set_xticks(range(len(student_counts)))
                ax2.set_xticklabels(student_counts['姓名'], rotation=45, ha='right', fontsize=10)
                ax2.set_xlabel('学生', fontsize=12, fontweight='bold')
                ax2.set_ylabel('互动次数', fontsize=12, fontweight='bold')
                ax2.set_title('学生参与度分布', fontsize=14, fontweight='bold', pad=15)
                
                # 删除上边框和右边框
                ax2.spines['top'].set_visible(False)
                ax2.spines['right'].set_visible(False)
                
                # 设置网格线仅在y轴
                ax2.grid(True, linestyle='--', alpha=0.7, axis='y')
                
                # 背景颜色
                ax2.set_facecolor('#F8F9FA')
                fig2.patch.set_facecolor('#FFFFFF')
                
                # 添加数值标签
                for bar in bars:
                    height = bar.get_height()
                    ax2.text(
                        bar.get_x() + bar.get_width()/2.,
                        height + 0.1,
                        f'{int(height)}',
                        ha='center', va='bottom',
                        fontweight='bold'
                    )
                
                st.pyplot(fig2)
            else:
                custom_message("暂无学生参与分布数据", "info")
            
            st.markdown("</div>", unsafe_allow_html=True)
        else:
            custom_message("尚无互动记录。启动监听并等待学生互动，或使用'模拟接收信号'按钮生成示例数据。", "info")
        
        # 最近互动记录
        st.markdown("<h3 class='sub-header'>最近互动记录</h3>", unsafe_allow_html=True)
        
        if st.session_state.interaction_records:
            # 显示最近的10条记录
            recent_records = pd.DataFrame(st.session_state.interaction_records[-10:])
            st.dataframe(recent_records, use_container_width=True)
        else:
            custom_message("尚无互动记录", "info")

if __name__ == "__main__":
    main() 